import type { UploadFile } from 'element-plus'
import imageCompression from 'browser-image-compression'
import Compressor from 'compressorjs'
import Cropper from 'cropperjs'
import { ElMessage } from 'element-plus'
import fileSaver from 'file-saver'
import JSZip from 'jszip'

const { saveAs } = fileSaver
export interface SelectedImage extends UploadFile {
  checked?: boolean // 自定义属性，用于选中状态
}

export class ImageCropper {
  private parseFileNameAndExtension(fileName: string) {
    // 检查文件名是否包含点（.）
    const lastDotIndex = fileName.lastIndexOf('.')
    // 如果没有点，或者点是文件名的第一个字符（如隐藏文件），则没有扩展名
    if (lastDotIndex <= 0) {
      return [fileName, '']
    }
    // 提取文件名（不带扩展名）和扩展名
    const name = fileName.substring(0, lastDotIndex)
    const extension = fileName.substring(lastDotIndex + 1)
    return [name, extension]
  }

  private getUniqueFileName(zip: JSZip, baseName: string, extension: string): string {
    let fileName = `${baseName}.${extension}`
    let counter = 1
    // 检查文件名是否已存在
    while (zip.file(fileName)) {
      fileName = `${baseName} (${counter}).${extension}`
      counter++
    }
    return fileName
  }

  public async downloadImages(fileList: SelectedImage[]) {
    const zip = new JSZip()
    // 并行下载文件
    const filesToAdd = await Promise.all(
      fileList.map(async (file) => {
        if (!file.checked)
          return null
        const response = await fetch(file.url as string)
        const blob = await response.blob()
        const filename = file.name
        return { filename, blob }
      }),
    )
    // 顺序添加到 ZIP
    for (const file of filesToAdd) {
      if (file) {
        const fileNameExtension = this.parseFileNameAndExtension(file.filename)
        if (Array.isArray(fileNameExtension)) {
          const uniqueFileName = this.getUniqueFileName(zip, fileNameExtension[0] as string, fileNameExtension[1] as string)
          zip.file(uniqueFileName, file.blob)
        }
      }
    }
    zip.generateAsync({ type: 'blob' }).then((content) => {
      saveAs(content, 'images.zip')
      ElMessage.success('打包下载成功')
    })
  }

  public async compressImage(file: SelectedImage, option: { [key: string]: any }): Promise<SelectedImage> {
    return new Promise((resolve, reject) => {
      const op = {
        ...option,
        success(result: Blob) {
          const raw = new File([result], file.name, {
            type: result.type,
            lastModified: Date.now(),
          })
          const selectedImage = <SelectedImage>({
            ...file,
            ...{
              ...file.raw,
              raw,
            },
          })
          URL.revokeObjectURL(file.url as string)
          selectedImage.url = URL.createObjectURL(result)
          selectedImage.size = raw.size
          resolve(selectedImage)
        },
        error(err: any) {
          console.error('Error compressing image:', err)
          reject(file.url)
        },
      }
      // eslint-disable-next-line no-new
      new Compressor(file.raw as File, op)
    })
  }

  private async compressImageSize(file: SelectedImage, targetKB: number): Promise<SelectedImage> {
    const options = {
      maxSizeMB: targetKB / 1024, // 最大文件大小，MB
      maxIteration: 30,
      useWebWorker: true, // 使用Web Worker加速压缩
    }
    return new Promise((resolve, reject) => {
      imageCompression(file.raw as File, options).then((result: Blob) => {
        if (!file.checked)
          return
        const raw = new File([result], file.name, {
          type: result.type,
          lastModified: Date.now(),
        })
        const selectedImage = <SelectedImage>({
          ...file,
          ...{
            ...file.raw,
            raw,
          },
        })
        URL.revokeObjectURL(file.url as string)
        selectedImage.url = URL.createObjectURL(result)
        selectedImage.size = raw.size
        resolve(selectedImage)
      }).catch((error) => {
        console.error('Error compressing image:', error)
        reject(file)
      })
    })
  }

  public async compressImageToKB(files: SelectedImage[], targetKB: number) {
    const imgs = <SelectedImage[]>([])
    for (const file of files) {
      if (!file.checked)
        continue
      const compressedFile = await this.compressImageSize(file, targetKB)
      imgs.push(compressedFile)
    }
    return imgs
  }

  public async compressImageToPercentage(files: SelectedImage[], percentage: number, imageRefs: { [key: string]: HTMLImageElement }) {
    // 实现压缩到指定百分比的逻辑
    // ... 具体实现 ...
    const imgs = <SelectedImage[]>([])
    for (const file of files) {
      if (!file.checked)
        continue
      const ref = imageRefs[file.uid]
      const newWidth = ref!.naturalWidth * percentage / 100
      const newHeight = ref!.naturalHeight * percentage / 100
      const option = {
        maxWidth: newWidth,
        maxHeight: newHeight,
      }
      const newfile = await this.compressImage(file, option)
      imgs.push(newfile)
    }
    return imgs
  }

  public async compressImageToMaxSide(files: SelectedImage[], maxSide: number) {
    // 实现压缩到最大边长的逻辑
    // ... 具体实现 ...
    const imgs = <SelectedImage[]>([])
    // 实现压缩到指定 KB 的逻辑
    const option = {
      maxWidth: maxSide,
      maxHeight: maxSide,
    }
    for (const file of files) {
      if (!file.checked)
        continue
      const newfile = await this.compressImage(file, option)
      imgs.push(newfile)
    }
    return imgs
  }

  public async compressImageToWidth(files: SelectedImage[], width: number) {
    // 实现压缩到指定宽度的逻辑
    // ... 具体实现 ...
    const imgs = <SelectedImage[]>([])
    // 实现压缩到指定 KB 的逻辑
    const option = {
      maxWidth: width,
    }
    for (const file of files) {
      if (!file.checked)
        continue
      const newfile = await this.compressImage(file, option)
      imgs.push(newfile)
    }
    return imgs
  }

  public async compressImageToHeight(files: SelectedImage[], height: number) {
    // 实现压缩到指定高度的逻辑
    // ... 具体实现 ...
    const imgs = <SelectedImage[]>([])
    // 实现压缩到指定 KB 的逻辑
    const option = {
      maxHeight: height,
    }
    for (const file of files) {
      if (!file.checked)
        continue
      const newfile = await this.compressImage(file, option)
      imgs.push(newfile)
    }
    return imgs
  }

  private getMimeTypeFromName(filename: string): string {
    const extension = filename.split('.').pop()?.toLowerCase() || ''
    const typeMap: { [key: string]: string } = {
      jpg: 'image/jpeg',
      jpeg: 'image/jpeg',
      png: 'image/png',
      gif: 'image/gif',
      webp: 'image/webp',
      bmp: 'image/bmp',
      ico: 'image/x-icon',
      svg: 'image/svg+xml',
      tiff: 'image/tiff',
    }
    return typeMap[extension] || 'application/octet-stream'
  }

  public async cropImageByRatio(files: SelectedImage[], ratio: [number, number], imageRefs: { [key: string]: HTMLImageElement }) {
    const imgs = <SelectedImage[]>([])

    for (const file of files) {
      if (!file.checked)
        continue

      const ref = imageRefs[file.uid]
      const cropper = new Cropper(ref as HTMLImageElement, {
        aspectRatio: ratio[0] / ratio[1],
        autoCropArea: 1,
        checkCrossOrigin: false, // Add this to handle CORS issues
      })

      // Wait for cropper to be ready
      await new Promise(resolve => setTimeout(resolve, 100))

      try {
        const canvas = cropper.getCroppedCanvas()
        if (!canvas) {
          throw new Error('Failed to create canvas')
        }

        // Convert canvas to blob and process
        const blob = await new Promise<Blob | null>(resolve =>
          canvas.toBlob(resolve),
        )

        if (!blob) {
          throw new Error('Canvas toBlob failed')
        }

        // Create new file reference
        const raw = new File([blob], file.name, {
          type: this.getMimeTypeFromName(file.name) || blob.type,
          lastModified: Date.now(),
        })

        const selectedImage = {
          ...file,
          raw,
          url: URL.createObjectURL(blob),
          size: raw.size,
        } as SelectedImage

        URL.revokeObjectURL(file.url!)
        const newImage = await this.compressImage(selectedImage, {})
        imgs.push(newImage)
      }
      finally {
        cropper.destroy()
      }
    }

    return imgs
  }
}
